#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/videodev2.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#include "config.h"
#include "video_recv_manager.h"
#include "debug_manager.h"

#define BUFFER_SIZE 1024 /**< 服务器从客户端中接收的最大数据字节数 */

/*!
 * @brief  与服务器建立连接
 * @param[in] socket_client  socket的句柄
 * @param[in] ip  服务器的ip
 * @return int 成功：0，失败：-1
 */
static int connect_to_server(int *socket_client, const char *ip)
{
	int ret;
	struct sockaddr_in socket_server_addr;

	*socket_client = socket(AF_INET, SOCK_STREAM, 0);

	socket_server_addr.sin_family      = AF_INET;
	socket_server_addr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	//socket_server_addr.sin_addr.s_addr = INADDR_ANY;
 	if (0 == inet_aton(ip, &socket_server_addr.sin_addr))
 	{
		DBG_PRINTF("invalid server_ip\n");
		return -1;
	}
	memset(socket_server_addr.sin_zero, 0, 8);

	ret = connect(*socket_client, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));	
	if (-1 == ret)
	{
		DBG_PRINTF("connect error!\n");
		return -1;
	}
	
	return 0;
}

/*!
 * @brief  断开与服务器的连接
 * @param[in] socket_client  socket的句柄
 * @return int 0
 */
static int disconnect_to_server(int *socket_client)
{
	close(*socket_client);

	return 0;
}

/*!
 * @brief  发送报文给服务器端，告诉其需要发送的数据类型与要求
 * @param[in] socket_client  socket的句柄
 * @return int 成功：总接收到的数据长度，失败：-1
 */
static int init(int *socket_client)
{
	char send_buf[100];
	int  send_len;

	int  recv_len;
	char recv_buf[1000];

	/*!
	 * 发请求类型字符串："GET /?action=stream\n"表示需要客户端接收视频流数据 
	 */
	memset(send_buf, 0x0, 100);
	strcpy(send_buf, "GET /?action=stream\n");
	send_len = send(*socket_client, send_buf, strlen(send_buf), 0);
	if (send_len <= 0)
	{
		close(*socket_client);
		return -1;
	}

	/*!
	 * 如果我们不使用密码功能!则只需发送任意长度为小于2字节的字符串 
	 */
	memset(send_buf, 0x0, 100);
	strcpy(send_buf, "f\n");
	send_len = send(*socket_client, send_buf, strlen(send_buf), 0);
	if (send_len <= 0)
	{
		close(*socket_client);
		return -1;
	}

	/*!
	 * 将从服务器端接收一次报文
	 * 由于之前已经做好准备工作，所以此时接收的信息是服务器端已ok
	 */
	/* 接收客户端发来的数据并显示出来 */
	recv_len = recv(*socket_client, recv_buf, 999, 0);
	if (recv_len <= 0)
	{
		close(*socket_client);
		return -1;
	}
	else
	{
		recv_buf[recv_len] = '\0';
		printf("http header: %s\n", recv_buf);
	}

	return 0;
}

/*!
 * @brief  返回视频数据的格式
 * @return int V4L2_PIX_FMT_MJPEG
 */
static int getformat(void)
{
	/* 直接返回视频的格式 */
	return V4L2_PIX_FMT_MJPEG;
}

/*!
 * @brief  解析服务器端发送的报文，获取一帧视频数据的大小
 * @param[in]  socket_client socket的句柄
 * @param[out] free_buf 存储服务器端发送的一帧视频数据的信息
 * @param[out] free_len free_buf中剩余内存大小
 * @return int 成功：一帧视频数据的大小
 */
static long int get_file_len(int *socket_client, char *free_buf, int *free_len)
{
	int recv_len;
	long int videolen;
	char recv_buf[1024];
	char *plen, *buffp;

	while(1)
	{
	    /*!
	     * 从服务器接收数据（将要接收到的一帧视频数据大小）
	     */
		recv_len = recv(*socket_client, recv_buf, 1024, 0);
		if (recv_len <= 0)
		{
			close(*socket_client);
			return -1;
		}

		/*!
		 * 解析recv_buf，判断接收到的数据是否是报文 
		 */
		plen = strstr(recv_buf, "Length:");
		if(NULL != plen)
		{
			plen = strchr(plen, ':');
			plen++;
			videolen = atol(plen);
			printf("the Video Len %ld\n", videolen);
		}

        /*!
         * 解析完毕的标志
         */
		buffp = strstr(recv_buf, "\r\n\r\n");
		if(buffp != NULL)
			break;
	}

    
	buffp += 4;
	*free_len = 1024 - (buffp - recv_buf);
	memcpy(free_buf, buffp, *free_len);

	return videolen;
}

/*!
 * @brief  从http客户端接收一帧视频数据数据
 * @param[in] socket_client  socket的句柄
 * @param[out] lpbuff  存储接收到数据的地址
 * @param[in] size  需要接收到的数据长度 
 * @return long 成功：总接收到的数据长度，失败：-1
 */
static long int http_recv(int *socket_client, char **lpbuff, long int size)
{
	int recv_len = 0;   /**< 一次从客户端接收到数据的长度 */
    int recv_sum = 0;   /**< 总共从客户端接收到数据的长度 */
	char recv_buf[BUFFER_SIZE]; /**< 存储接收到的数据 */

    /*!
     * 分次接收数据，最多接收BUFFER_SIZE大小字节的数据
     */
	while(size > 0)
	{
	    /*!
	     * 调用recv从客户端接收数据
	     * 大小：(size > BUFFER_SIZE)? BUFFER_SIZE: size
	     * 数据存储到：recv_buf[BUFFER_SIZE]
	     */
		recv_len = recv(*socket_client, recv_buf, (size > BUFFER_SIZE)? BUFFER_SIZE: size, 0);
		if (recv_len <= 0)
			break;

		recv_sum += recv_len;   /* 实际接收的字节数 */
		size -= recv_len;       /* 剩余需要接收的字节数 */

        /*!
         * 判断传入的lpbuff是否为空
         * 空则分配内存，不空则扩大内存
         */
		if(*lpbuff == NULL)
		{
			*lpbuff = (char *)malloc(recv_sum);
			if(*lpbuff == NULL)
				return -1;
		}
		else
		{
			*lpbuff = (char *)realloc(*lpbuff, recv_sum);
			if(*lpbuff == NULL)
				return -1;
		}

        /*!
         * 根据偏移值计算出内存地址，拷贝数据
         */
		memcpy(((*lpbuff) + recv_sum - recv_len), recv_buf, recv_len);
	}

	return recv_sum;
}

/*!
 * @brief  获取一帧视频数据
 * @param[in] socket_client  socket的句柄
 * @param[in] video_buf  存储一帧数据的地址，需在函数外分配
 * @return 0 - 成功缩放，-1 - 不支持缩放
 */
static int get_video(int *socket_client, PT_VIDEOBUF video_buf)
{
	long int video_len, recv_len;
	int first_len = 0;
	char tmpbuf[1024];
	char *free_buffer = NULL;

    if (video_buf->pixel_data.PixelDatas == NULL)
    {
        DebugPrint(APP_ERR"please check that video_buf->pixel_data.PixelDatas == NULL\n");
        return -1;
    }

    /*!
     * 获取一帧视频数据
     */
	while(1)
	{
        /* 解析服务器的报文，获取一帧视频数据的大小 */
		video_len = get_file_len(socket_client, tmpbuf, &first_len); 

        /* 解析服务器的数据，获取已接收的视频数据的大小 */
		recv_len = http_recv(socket_client, &free_buffer, video_len - first_len);

        /* 原子操作 */
		pthread_mutex_lock(&video_buf->db);

		/* 将两次接收到的视频数据组装成一帧数据 */
		memcpy(video_buf->pixel_data.PixelDatas, tmpbuf, first_len);
		memcpy(video_buf->pixel_data.PixelDatas + first_len, free_buffer, recv_len);
		video_buf->pixel_data.TotalBytes = video_len;

        /* 发出一个数据更新的信号，通知输出通道来取数据 */
		pthread_cond_broadcast(&video_buf->db_update);

        /* 原子操作结束 */
		pthread_mutex_unlock(&video_buf->db);			
	}
    
	return 0;
}

/* 构造 */
static T_VIDEORECV s_video_recv = {
    .name        	     = "http",
    .ConnectToServer     = connect_to_server,
    .DisConnectToServer  = disconnect_to_server,
    .Init 				 = init,
    .GetFormat		     = getformat,
    .GetVideo			 = get_video,
};

/* 注册 */
int video_recv_init(void)
{
    return RegisterVideoRecv(&s_video_recv);
}

